// RedJey Graphics User Interface Library for J2ME Platform.
// Copyright (C) 2008 Yevgen Krapiva
//
// This library is free software; you can redistribute
// it and/or modify it under the terms of the
// GNU Lesser General Public License as published
// by the Free Software Foundation; either version 3.0
// of the License, or any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//

package com.incrediblesoft.redjey;

/**
 * @author Yevgen Krapiva<br>
 *         Homepage: http://code.google.com/p/redjey/<br>
 *         Feedback: ykrapiva@gmail.com<br>
 *         Copyright (C) 2008 Yevgen Krapiva<br>
 *
 * Code used in this class was taken from java.awt package.
 * Later was modified. 
 */

public class Rectangle
{
    public int x, y;
    public int width, height;

    public Rectangle()
    {
        this(0, 0, 0, 0);
    }

    public Rectangle(int width, int height)
    {
        this(0, 0, width, height);
    }

    public Rectangle(int x, int y, int width, int height)
    {
        setRect(x, y, width, height);
    }

    public Rectangle(Rectangle rect)
    {
        this(rect.x, rect.y, rect.width, rect.height);
    }

    public int getX()
    {
        return x;
    }

    public int getY()
    {
        return y;
    }

    public int getWidth()
    {
        return width;
    }

    public int getHeight()
    {
        return height;
    }

    public void setLocation(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public void setSize(int width, int height)
    {
        this.width = width;
        this.height = height;
    }

    public void setRect(int x, int y, int width, int height)
    {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    public boolean intersects(Rectangle r)
    {
        return Intersects(x, y, width, height, r.x, r.y, r.width, r.height);
    }

    public boolean intersects(int x, int y, int width, int height)
    {
        return Intersects(this.x, this.y, this.width, this.height, x, y, width, height);
    }

    public static boolean Intersects(Rectangle r1, Rectangle r2)
    {
        return Intersects(r1.x, r1.y, r1.width, r1.height, r2.x, r2.y, r2.width, r2.height);
    }

    public static boolean Intersects(int r1x, int r1y, int r1w, int r1h, int r2x, int r2y, int r2w, int r2h)
    {
        if (r2w <= 0 || r2h <= 0 || r1w <= 0 || r1h <= 0)
            return false;

        r2w += r2x;
        r2h += r2y;
        r1w += r1x;
        r1h += r1y;
        //      overflow || intersect
        return ((r2w < r2x || r2w > r1x) &&
                (r2h < r2y || r2h > r1y) &&
                (r1w < r1x || r1w > r2x) &&
                (r1h < r1y || r1h > r2y));
    }

    public boolean contains(Rectangle rect)
    {
        return contains(rect.x, rect.y, rect.width, rect.height);
    }

    public boolean contains(int X, int Y, int W, int H)
    {
        int w = this.width;
        int h = this.height;
        if ((w | h | W | H) < 0)
        {
            // At least one of the dimensions is negative...
            return false;
        }
        // Note: if any dimension is zero, tests below must return false...
        int x = this.x;
        int y = this.y;
        if (X < x || Y < y)
        {
            return false;
        }
        w += x;
        W += X;
        if (W <= X)
        {
            // X+W overflowed or W was zero, return false if...
            // either original w or W was zero or
            // x+w did not overflow or
            // the overflowed x+w is smaller than the overflowed X+W
            if (w >= x || W > w) return false;
        }
        else
        {
            // X+W did not overflow and W was not zero, return false if...
            // original w was zero or
            // x+w did not overflow and x+w is smaller than X+W
            if (w >= x && W > w) return false;
        }
        h += y;
        H += Y;
        if (H <= Y)
        {
            if (h >= y || H > h) return false;
        }
        else
        {
            if (h >= y && H > h) return false;
        }
        return true;
    }

    public boolean inside(int X, int Y)
    {
        return inside(this, X, Y);
    }

    public static boolean inside(Rectangle rect, int X, int Y)
    {
        int w = rect.width;
        int h = rect.height;
        if ((w | h) < 0)
        {
            // At least one of the dimensions is negative...
            return false;
        }
        // Note: if either dimension is zero, tests below must return false...
        int x = rect.x;
        int y = rect.y;
        if (X < x || Y < y)
        {
            return false;
        }
        w += x;
        h += y;
        //    overflow || intersect
        return ((w < x || w > X) &&
                (h < y || h > Y));
    }

    /**
     * Computes the intersection of this <code>Rectangle</code> with the
     * specified <code>Rectangle</code>. Returns a new <code>Rectangle</code>
     * that represents the intersection of the two rectangles.
     * If the two rectangles do not intersect, the result will be
     * an empty rectangle.
     *
     * @param r the specified <code>Rectangle</code>
     * @return the largest <code>Rectangle</code> contained in both the
     *         specified <code>Rectangle</code> and in
     *         this <code>Rectangle</code>; or if the rectangles
     *         do not intersect, an empty rectangle.
     */
    public Rectangle intersection(Rectangle r)
    {
        int tx1 = this.x;
        int ty1 = this.y;
        int rx1 = r.x;
        int ry1 = r.y;
        long tx2 = tx1;
        tx2 += this.width;
        long ty2 = ty1;
        ty2 += this.height;
        long rx2 = rx1;
        rx2 += r.width;
        long ry2 = ry1;
        ry2 += r.height;
        if (tx1 < rx1) tx1 = rx1;
        if (ty1 < ry1) ty1 = ry1;
        if (tx2 > rx2) tx2 = rx2;
        if (ty2 > ry2) ty2 = ry2;
        tx2 -= tx1;
        ty2 -= ty1;
        // tx2,ty2 will never overflow (they will never be
        // larger than the smallest of the two source w,h)
        // they might underflow, though...
        if (tx2 < Integer.MIN_VALUE) tx2 = Integer.MIN_VALUE;
        if (ty2 < Integer.MIN_VALUE) ty2 = Integer.MIN_VALUE;
        return new Rectangle(tx1, ty1, (int) tx2, (int) ty2);
    }

    /**
     * Computes the union of this <code>Rectangle</code> with the
     * specified <code>Rectangle</code>. Returns a new
     * <code>Rectangle</code> that
     * represents the union of the two rectangles.
     * <p/>
     * If either {@code Rectangle} has any dimension less than zero
     * the rules for <a href=#NonExistant>non-existant</a> rectangles
     * apply.
     * If only one has a dimension less than zero, then the result
     * will be a copy of the other {@code Rectangle}.
     * If both have dimension less than zero, then the result will
     * have at least one dimension less than zero.
     * <p/>
     * If the resulting {@code Rectangle} would have a dimension
     * too large to be expressed as an {@code int}, the result
     * will have a dimension of {@code Integer.MAX_VALUE} along
     * that dimension.
     *
     * @param r the specified <code>Rectangle</code>
     * @return the smallest <code>Rectangle</code> containing both
     *         the specified <code>Rectangle</code> and this
     *         <code>Rectangle</code>.
     */
    public Rectangle union(Rectangle r)
    {
        long tx2 = this.width;
        long ty2 = this.height;
        if ((tx2 | ty2) < 0)
        {
            // This rectangle has negative dimensions...
            // If r has non-negative dimensions then it is the answer.
            // If r is non-existant (has a negative dimension), then both
            // are non-existant and we can return any non-existant rectangle
            // as an answer.  Thus, returning r meets that criterion.
            // Either way, r is our answer.
            return new Rectangle(r);
        }
        long rx2 = r.width;
        long ry2 = r.height;
        if ((rx2 | ry2) < 0)
        {
            return new Rectangle(this);
        }
        int tx1 = this.x;
        int ty1 = this.y;
        tx2 += tx1;
        ty2 += ty1;
        int rx1 = r.x;
        int ry1 = r.y;
        rx2 += rx1;
        ry2 += ry1;
        if (tx1 > rx1) tx1 = rx1;
        if (ty1 > ry1) ty1 = ry1;
        if (tx2 < rx2) tx2 = rx2;
        if (ty2 < ry2) ty2 = ry2;
        tx2 -= tx1;
        ty2 -= ty1;
        // tx2,ty2 will never underflow since both original rectangles
        // were already proven to be non-empty
        // they might overflow, though...
        if (tx2 > Integer.MAX_VALUE) tx2 = Integer.MAX_VALUE;
        if (ty2 > Integer.MAX_VALUE) ty2 = Integer.MAX_VALUE;
        return new Rectangle(tx1, ty1, (int) tx2, (int) ty2);
    }

    public boolean isEmpty()
    {
        return (width <= 0) || (height <= 0);
    }


    public String toString()
    {
        return "Rect(" + x + ", " + y + ", " + width + ", " + height + ")";
    }
}
